/*
 * Copyright 2009 Griffin Brown Digital Publishing Ltd
 * All rights reserved.
 *
 * This file is part of Probatron.
 *
 * Probatron is free software: you can redistribute it and/or modify
 * it under the terms of the Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * Probatron is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * Affero General Public License for more details.
 *
 * You should have received a copy of the Affero General Public License
 * along with Probatron.  If not, see <http://www.gnu.org/licenses/>.
 */

/*
 * $Header: /TEST/probatron/com/griffinbrown/shail/test/XPathTestBase.java,v 1.1 2009/01/08 14:41:29 GBDP\andrews Exp $
 * $Revision: 1.1 $
 * $Date: 2009/01/08 14:41:29 $
 *
 * ====================================================================
 *
 * Copyright 2000-2002 bob mcwhirter & James Strachan.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 * 
 *   * Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer.
 * 
 *   * Redistributions in binary form must reproduce the above copyright
 *     notice, this list of conditions and the following disclaimer in the
 *     documentation and/or other materials provided with the distribution.
 * 
 *   * Neither the name of the Jaxen Project nor the names of its
 *     contributors may be used to endorse or promote products derived 
 *     from this software without specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * ====================================================================
 * This software consists of voluntary contributions made by many
 * individuals on behalf of the Jaxen Project and was originally
 * created by bob mcwhirter <bob@werken.com> and
 * James Strachan <jstrachan@apache.org>.  For more information on the
 * Jaxen Project, please see <http://www.jaxen.org/>.
 *
 * @version $Id: XPathTestBase.java,v 1.1 2009/01/08 14:41:29 GBDP\andrews Exp $
 */

/**
 * ADAPTED FROM org.jaxen.test.XPathTestBase
 */

package org.probatron.test;

import java.util.List;

import junit.framework.TestCase;

import org.apache.log4j.Logger;
import org.probatron.ShailXPath;
import org.probatron.jaxen.Context;
import org.probatron.jaxen.ContextSupport;
import org.probatron.jaxen.JaxenException;
import org.probatron.jaxen.Navigator;
import org.probatron.jaxen.SimpleNamespaceContext;
import org.probatron.jaxen.SimpleVariableContext;
import org.probatron.jaxen.XPathFunctionContext;
import org.probatron.jaxen.function.StringFunction;
import org.probatron.jaxen.saxpath.helpers.XPathReaderFactory;

import com.griffinbrown.shail.util.ShailIterator;
import com.griffinbrown.shail.util.ShailList;

public abstract class XPathTestBase extends TestCase
{
    protected static String VAR_URI = "http://jaxen.org/test-harness/var";
    protected static String TESTS_XML = "xml/test/tests.xml";

    protected static boolean verbose = false;
    protected static boolean debug = false;
    private ContextSupport contextSupport;

    protected static Logger logger = Logger.getLogger( XPathTestBase.class );


    protected XPathTestBase( String name )
    {
        super( name );
    }


    protected void setUp() throws Exception
    {
        this.contextSupport = null;
        System.setProperty( XPathReaderFactory.DRIVER_PROPERTY, "" );
        log( "-----------------------------" );
    }


    public void log( String text )
    {
        logger.debug( text );
    }


    protected void assertCountXPath( int expectedSize, int context, String xpathStr )
            throws JaxenException
    {
        assertCountXPath2( expectedSize, context, xpathStr );
    }

    /**
     * (Note this has been modified to construct ShailXPaths)
     * @param expectedSize
     * @param context
     * @param xpathStr
     * @return
     * @throws JaxenException
     */
    private int assertCountXPath2( int expectedSize, int context, String xpathStr )
            throws JaxenException
    {
        logger.debug( "  Select :: " + xpathStr );
        ShailXPath xpath = new ShailXPath( xpathStr );
        ShailList results = (ShailList)xpath.selectNodes( getContext( context ) );
//        logger.debug( "results="+results );
//        logger.debug( "RETURN TYPE="+results.getClass() );
        logger.debug( "    Expected Size :: " + expectedSize );
        logger.debug( "    Result Size   :: " + results.size() );
        if( expectedSize != results.size() )
        {
            logger.debug( "      ## FAILED" );
            logger.debug( "      ## xpath: " + xpath + " = " + xpath.debug() );
            ShailIterator resultIter = (ShailIterator)results.iterator();
            while( resultIter.hasNext() )
            {
                logger.debug( "      --> " + resultIter.nextNode() );
            }
        }
        assertEquals( xpathStr, expectedSize, results.size() );
        if( expectedSize > 0 )
        {
            return results.getInt( 0 );
        }
        return -1;
    }


    private void assertInvalidXPath( int context, String xpathStr )
    {
        try
        {
            logger.debug( "  Select :: " + xpathStr );
            ShailXPath xpath = new ShailXPath( xpathStr );
            List results = xpath.selectNodes( getContext( context ) );
            logger.debug( "    Result Size   :: " + results.size() );
            fail( "An exception was expected." );
        }
        catch( JaxenException e )
        {
            logger.debug( "    Caught expected exception " + e.getMessage() );
        }
    }


    protected void assertValueOfXPath( String expected, int context, String xpathStr )
            throws JaxenException
    {
        ShailXPath xpath = new ShailXPath( xpathStr );
        Object node = xpath.evaluate( getContext( context ) );
        
        String result = StringFunction.evaluate( node, getNavigator() );
        logger.debug( "  Select :: " + xpathStr );
        logger.debug( "    Expected :: " + expected );
        logger.debug( "    Result   :: " + result );
        if( ! expected.equals( result ) )
        {
            logger.debug( "      ## FAILED" );
            logger.debug( "      ## xpath: " + xpath + " = " + xpath.debug() );
        }
        assertEquals( xpathStr, expected, result );
    }


    private Context getContext( int contextNode )
    {
        Context context = new Context( getContextSupport() );
        ShailList list = new ShailList( 1 );
        list.addInt( contextNode );
        context.setNodeSet( list );
        logger.debug("context set to "+list);
        return context;
    }


    private ContextSupport getContextSupport()
    {
        if( this.contextSupport == null )
        {
            this.contextSupport = new ContextSupport( new SimpleNamespaceContext(),
                    XPathFunctionContext.getInstance(), new SimpleVariableContext(),
                    getNavigator() );
        }
        return this.contextSupport;
    }


    protected abstract Navigator getNavigator();


    protected abstract int getDocument( String url ) throws Exception;

/*
    public void testGetNodeType() throws FunctionCallException, UnsupportedAxisException
    {
        Navigator nav = getNavigator();
        int document = nav.getDocument( "xml/testNamespaces.xml" );
        int count = 0;
        ShailIterator descendantOrSelfAxisIterator = (ShailIterator)nav.getDescendantOrSelfAxisIterator( document );
        while( descendantOrSelfAxisIterator.hasNext() )
        {
            int node = descendantOrSelfAxisIterator.nextNode();
            ShailIterator namespaceAxisIterator = (ShailIterator)nav.getNamespaceAxisIterator( node );
            while( namespaceAxisIterator.hasNext() )
            {
                count++;
                int ns = namespaceAxisIterator.nextNode();
//                logger.debug( "NAMESPACE NODE: "+ns );
                assertEquals( "Node type mismatch", Pattern.NAMESPACE_NODE, nav
                        .getNodeType( ns ) );
            }
        }
        assertEquals( 25, count );
    }*/


    /* test for jaxen-24
     */
    public void testJaxen24() throws JaxenException
    {
        Navigator nav = getNavigator();
        String url = "xml/jaxen24.xml";
        log( "Document [" + url + "]" );
        int document = nav.getDocument( url );
        ShailXPath contextpath = new ShailXPath( "/body/div", nav );
        log( "Initial Context :: " + contextpath );
        List list = contextpath.selectNodes( document );
        ShailIterator iter = (ShailIterator)list.iterator();
        while( iter.hasNext() )
        {
            int context = iter.nextNode();
            assertCountXPath( 1, context, "preceding::*[1]" );
            assertValueOfXPath( "span", context, "local-name(preceding::*[1])" );
        }
    }


    /* jaxen-58
     */
    public void testJaxen58() throws JaxenException
    {
        Navigator nav = getNavigator();
        String url = "xml/jaxen24.xml";
        log( "Document [" + url + "]" );
        int document = nav.getDocument( url );
        ShailXPath contextpath = new ShailXPath( "/", nav );
        log( "Initial Context :: " + contextpath );
        List list = contextpath.selectNodes( document );
        ShailIterator iter = (ShailIterator)list.iterator();
        while( iter.hasNext() )
        {
            int context = iter.nextNode();
            assertCountXPath( 0, context, "//preceding::x" );
            assertCountXPath( 0, context, "//following::x" );
            assertCountXPath( 0, context, "/descendant::*/preceding::x" );
            assertCountXPath( 0, context, "/descendant::node()/preceding::x" );
        }
    }


    /* test for jaxen-3
     */
    public void testJaxen3() throws JaxenException
    {
        Navigator nav = getNavigator();
        String url = "xml/simple.xml";
        log( "Document [" + url + "]" );
        int document = nav.getDocument( url );
        ShailXPath contextpath = new ShailXPath( "/", nav );
        log( "Initial Context :: " + contextpath );
        List list = contextpath.selectNodes( document );
        ShailIterator iter = (ShailIterator)list.iterator();
        while( iter.hasNext() )
        {
            int context = iter.nextNode();
            assertValueOfXPath( "abd", context, "string()" );
        }
    }


    public void testStringFunction1() throws JaxenException
    {
        Navigator nav = getNavigator();
        String url = "xml/simple.xml";
        log( "Document [" + url + "]" );
        int document = nav.getDocument( url );
        ShailXPath contextpath = new ShailXPath( "/root", nav );
        log( "Initial Context :: " + contextpath );
        List list = contextpath.selectNodes( document );
        ShailIterator iter = (ShailIterator)list.iterator();
        while( iter.hasNext() )
        {
            int context = iter.nextNode();
            assertValueOfXPath( "abd", context, "string()" );
        }
    }


    public void testStringFunction2() throws JaxenException
    {
        Navigator nav = getNavigator();
        String url = "xml/simple.xml";
        log( "Document [" + url + "]" );
        int document = nav.getDocument( url );
        ShailXPath contextpath = new ShailXPath( "/root/a", nav );
        log( "Initial Context :: " + contextpath );
        List list = contextpath.selectNodes( document );
        ShailIterator iter = (ShailIterator)list.iterator();
        while( iter.hasNext() )
        {
            int context = iter.nextNode();
            assertValueOfXPath( "a", context, "string()" );
        }
    }


    public void testStringFunction3() throws JaxenException
    {
        Navigator nav = getNavigator();
        String url = "xml/simple.xml";
        log( "Document [" + url + "]" );
        int document = nav.getDocument( url );
        ShailXPath contextpath = new ShailXPath( "/root/c", nav );
        log( "Initial Context :: " + contextpath );
        List list = contextpath.selectNodes( document );
        ShailIterator iter = (ShailIterator)list.iterator();
        while( iter.hasNext() )
        {
            int context = iter.nextNode();
            assertValueOfXPath( "d", context, "string()" );
        }
    }


    /* test for jaxen-3
     */
    public void testJaxen3dupe() throws JaxenException
    {
        Navigator nav = getNavigator();
        String url = "xml/jaxen3.xml";
        log( "Document [" + url + "]" );
        int document = nav.getDocument( url );
        ShailXPath contextpath = new ShailXPath( "/", nav );
        log( "Initial Context :: " + contextpath );
        List list = contextpath.selectNodes( document );

        ShailIterator iter = (ShailIterator)list.iterator();
        while( iter.hasNext() )
        {
            int context = iter.nextNode();
            assertCountXPath( 1, context,
                    "/Configuration/hostname/attrlist/hostname[. = 'CE-A']" );        
        }
    }


    /* parser test cases all of which should fail
     */
    public void testForParserErrors() throws JaxenException
    {
        Navigator nav = getNavigator();
        String url = "xml/numbers.xml";
        log( "Document [" + url + "]" );
        int document = nav.getDocument( url );
        ShailXPath contextpath = new ShailXPath( "/", nav );
        log( "Initial Context :: " + contextpath );
        List list = contextpath.selectNodes( document );
        ShailIterator iter = (ShailIterator)list.iterator();
        while( iter.hasNext() )
        {
            int context = iter.nextNode();
            /* repeated xpaths, jaxen-35
             */
            assertInvalidXPath( context, "/numbers numbers" );
            /* invalid xpath, jaxen-34
             */
            assertInvalidXPath( context, "/a/b[c > d]efg" );
            /* invalid xpath, jaxen-27
             */
            assertInvalidXPath( context, "/inv/child::" );
            /* invalid xpath, jaxen-26
             */
            assertInvalidXPath( context, "/invoice/@test[abcd" );
            assertInvalidXPath( context, "/invoice/@test[abcd > x" );
            /* unterminated string
             */
            assertInvalidXPath( context, "string-length('a" );
            /* various edge cases where code threw no exception
             */
            assertInvalidXPath( context, "/descendant::()" );
            assertInvalidXPath( context, "(1 + 1" );
            // no ! operator
            assertInvalidXPath( context, "!false()" );
        }
    }


    /* test cases for the use of underscores in names
     */
    public void testUnderscoresInNames() throws JaxenException
    {
        Navigator nav = getNavigator();
        String url = "xml/underscore.xml";
        log( "Document [" + url + "]" );
        int document = nav.getDocument( url );
        ShailXPath contextpath = new ShailXPath( "/", nav );
        log( "Initial Context :: " + contextpath );
        List list = contextpath.selectNodes( document );
        ShailIterator iter = (ShailIterator)list.iterator();
        while( iter.hasNext() )
        {
            int context = iter.nextNode();
            assertCountXPath( 1, context, "/root/@a" );
            assertCountXPath( 1, context, "/root/@_a" );
            assertCountXPath( 1, context, "/root/b" );
            assertCountXPath( 1, context, "/root/_b" );
            assertValueOfXPath( "1", context, "/root/@a" );
            assertValueOfXPath( "2", context, "/root/@_a" );
            assertValueOfXPath( "1", context, "/root/b" );
            assertValueOfXPath( "2", context, "/root/_b" );
        }
    }


    /* test cases for the use of = with node-sets
     */
    public void testNodesetEqualsString() throws JaxenException
    {
        Navigator nav = getNavigator();
        String url = "xml/web.xml";
        log( "Document [" + url + "]" );
        int document = nav.getDocument( url );
        ShailXPath contextpath = new ShailXPath( "/", nav );
        log( "Initial Context :: " + contextpath );
        List list = contextpath.selectNodes( document );
        ShailIterator iter = (ShailIterator)list.iterator();
        while( iter.hasNext() )
        {
            int context = iter.nextNode();
            assertValueOfXPath( "true", context, "/web-app/servlet/servlet-name = 'file'" );
            assertValueOfXPath( "true", context, "/web-app/servlet/servlet-name = 'snoop'" );
        }
    }


    public void testNodesetEqualsNumber() throws JaxenException
    {
        Navigator nav = getNavigator();
        String url = "xml/numbers.xml";
        log( "Document [" + url + "]" );
        int document = nav.getDocument( url );
        ShailXPath contextpath = new ShailXPath( "/", nav );
        log( "Initial Context :: " + contextpath );
        List list = contextpath.selectNodes( document );
        ShailIterator iter = (ShailIterator)list.iterator();
        while( iter.hasNext() )
        {
            int context = iter.nextNode();
            assertValueOfXPath( "true", context, "/numbers/set/nr = '-3'" );
            assertValueOfXPath( "true", context, "/numbers/set/nr = -3" );
            assertValueOfXPath( "true", context, "/numbers/set/nr = 24" );
            assertValueOfXPath( "true", context, "/numbers/set/nr/@value = '9999'" );
            assertValueOfXPath( "true", context, "/numbers/set/nr/@value = 9999.0" );
            assertValueOfXPath( "true", context, "/numbers/set/nr/@value = 66" );
        }
    }


    /* test basic math...
     */
    public void testIntegerArithmetic() throws JaxenException
    {
        Navigator nav = getNavigator();
        String url = "xml/numbers.xml";
        log( "Document [" + url + "]" );
        int document = nav.getDocument( url );
        ShailXPath contextpath = new ShailXPath( "/", nav );
        log( "Initial Context :: " + contextpath );
        List list = contextpath.selectNodes( document );
        ShailIterator iter = (ShailIterator)list.iterator();
        while( iter.hasNext() )
        {
            int context = iter.nextNode();
            assertValueOfXPath( "true", context, "(8 * 2 + 1) = 17" );
            assertValueOfXPath( "true", context, "(1 + 8 * 2) = 17" );
            assertValueOfXPath( "true", context, "(7 - 3 + 1) = 5" );
            assertValueOfXPath( "true", context, "(8 - 4 + 5 - 6) = 3" );
            /* left-assoc tests, comments show WRONG evaluation
             */
            /* 3 - 2 - 1 != 2
             */
            assertValueOfXPath( "0", context, "3 - 2 - 1" );
            /* 8 div 4 div 2 != 4
             */
            assertValueOfXPath( "1", context, "8 div 4 div 2" );
            /* 3 mod 5 mod 7 != 1
             */
            assertValueOfXPath( "3", context, "3 mod 7 mod 5" );
            /* 1=(2=2) is true
             */
            assertValueOfXPath( "false", context, "1 = 2 = 2" );
            /*  2!=(3!=1) => 2!=1 => true, (2!=3)!=1 => 1!=1 => false
             */
            assertValueOfXPath( "false", context, "2 != 3 != 1" );
            /* 3 > (2 > 1) is true
             */
            assertValueOfXPath( "false", context, "3 > 2 > 1" );
            /* 3 >= (2 >= 2) is true
             */
            assertValueOfXPath( "false", context, "3 >= 2 >= 2" );
            /* 1 < (2 < 3) is false
             */
            assertValueOfXPath( "true", context, "1 < 2 < 3" );
            /* 0 <= (2 <= 3) is true
             */
            assertValueOfXPath( "true", context, "2 <= 2 <= 3" );
        }
    }


    public void testFloatingPointArithmetic() throws JaxenException
    {
        Navigator nav = getNavigator();
        String url = "xml/numbers.xml";
        log( "Document [" + url + "]" );
        int document = nav.getDocument( url );
        ShailXPath contextpath = new ShailXPath( "/", nav );
        log( "Initial Context :: " + contextpath );
        List list = contextpath.selectNodes( document );
        ShailIterator iter = (ShailIterator)list.iterator();
        while( iter.hasNext() )
        {
            int context = iter.nextNode();
            assertValueOfXPath( "true", context, "(8.5 * 2.0 + 1) = 18" );
            assertValueOfXPath( "true", context, "(1.00 + 8.5 * 2) = 18.0" );
            assertValueOfXPath( "true", context, "(7.1 - 7.1 + 1.5) = 1.5" );
            assertValueOfXPath( "true", context, "(8.000 - 4.0 + 5 - 6.00) = 3" );
            assertValueOfXPath( "0", context, "3.5 - 2.5 - 1.0" );
            assertValueOfXPath( "1", context, "8.0 div 4.0 div 2.0" );
            assertValueOfXPath( "3", context, "3.0 mod 7.0 mod 5.0" );
            assertValueOfXPath( "false", context, "1.5 = 2.3 = 2.3" );
            assertValueOfXPath( "false", context, "2.1 != 3.2 != 1.9" );
            assertValueOfXPath( "false", context, "3.8 > 2.7 > 1.6" );
            assertValueOfXPath( "false", context, "3.4 >= 2.5 >= 2.5" );
            assertValueOfXPath( "true", context, "1.4 < 2.3 < 3.2" );
            assertValueOfXPath( "true", context, "2.5 <= 2.5 <= 3.5" );
        }
    }


    /* test cases for preceding axis with different node types
     */
    public void testPrecedingSiblingAxis() throws JaxenException
    {
        Navigator nav = getNavigator();
        String url = "xml/pi2~.xml";
        log( "Document [" + url + "]" );
        int document = nav.getDocument( url );
        
//        ShailXPath foo = new ShailXPath( "/a/c/preceding-sibling::node()", nav );
//        if( foo.selectNodes( document ).size() != 5 )
//            throw new RuntimeException(""+foo.selectNodes( document ));
        
        ShailXPath contextpath = new ShailXPath( "/a/c", nav );
        log( "Initial Context :: " + contextpath );
        List list = contextpath.selectNodes( document );
        ShailIterator iter = (ShailIterator)list.iterator();
        while( iter.hasNext() )
        {
            int context = iter.nextNode();
            
            assertCountXPath( 1, context, "preceding-sibling::*" );            
            assertValueOfXPath( "foo", context, "preceding-sibling::*" );            
            assertCountXPath( 1, context, "//processing-instruction()" );
            assertCountXPath( 3, context, "preceding-sibling::text()" );
            assertCountXPath( 1, context, "preceding-sibling::processing-instruction()" );
            
            assertCountXPath( 5, context, "preceding-sibling::node()" );
            assertCountXPath( 2, context, "(preceding-sibling::*|preceding-sibling::processing-instruction())" );
            assertCountXPath( 3, context, "preceding-sibling::text()" );
            assertCountXPath( 1, context, "preceding-sibling::*[1]" );
            assertCountXPath( 1, context, "preceding-sibling::processing-instruction()" );
            assertValueOfXPath( "order-by=\"x\"", context,
                    "preceding-sibling::processing-instruction()" );
            assertValueOfXPath( "foo", context, "preceding-sibling::*[1]" );
            assertValueOfXPath( "order-by=\"x\"", context, "preceding-sibling::node()[2]" );
        }
    }


    public void testVariableLookup() throws JaxenException
    {
        Navigator nav = getNavigator();
        String url = "xml/id.xml";
        log( "Document [" + url + "]" );
        int document = nav.getDocument( url );
        ShailXPath contextpath = new ShailXPath( "/", nav );
        log( "Initial Context :: " + contextpath );
        List list = contextpath.selectNodes( document );
        SimpleVariableContext varContext = new SimpleVariableContext();
        varContext.setVariableValue( null, "foobar", "foobar" );
        varContext.setVariableValue( null, "foo", "foo" );
        getContextSupport().setVariableContext( varContext );
        ShailIterator iter = (ShailIterator)list.iterator();
        while( iter.hasNext() )
        {
            int context = iter.nextNode();
            assertValueOfXPath( "foobar", context, "$foobar" );
            assertCountXPath( 1, context, "/foo[@id=$foobar]" );
            assertCountXPath( 0, context, "/foo[@id='$foobar']" );
            assertCountXPath( 1, context, "/foo[concat($foo, 'bar')=@id]" );
            assertCountXPath( 0, context, "CD_Library/artist[@name=$artist]" );
        }
    }


    public void testAttributeParent() throws JaxenException
    {
        Navigator nav = getNavigator();
        String url = "xml/id.xml";
        log( "Document [" + url + "]" );
        int document = nav.getDocument( url );
        ShailXPath contextpath = new ShailXPath( "/", nav );
        log( "Initial Context :: " + contextpath );
        List list = contextpath.selectNodes( document );
        ShailIterator iter = (ShailIterator)list.iterator();
        while( iter.hasNext() )
        {
            int context = iter.nextNode();
            /* attributes have a parent: their element
             */
            assertCountXPath( 1, context, "/foo/@id/parent::foo" );
        }
    }


    /* attributes can also be used as context nodes
     */
    public void testAttributeAsContext() throws JaxenException
    {
        Navigator nav = getNavigator();
        String url = "xml/id.xml";
        log( "Document [" + url + "]" );
        int document = nav.getDocument( url );
        ShailXPath contextpath = new ShailXPath( "/foo/@id", nav );
        log( "Initial Context :: " + contextpath );
        List list = contextpath.selectNodes( document );
        
        ShailIterator iter = (ShailIterator)list.iterator();
        while( iter.hasNext() )
        {
            int context = iter.nextNode();
            assertCountXPath( 1, context, "parent::foo" );
        }
    }


    public void testid53992() throws JaxenException
    {
        Navigator nav = getNavigator();
        String url = "xml/pi.xml";
        log( "Document [" + url + "]" );
        int document = nav.getDocument( url );
        ShailXPath contextpath = new ShailXPath( "/", nav );
        log( "Initial Context :: " + contextpath );
        List list = contextpath.selectNodes( document );
        ShailIterator iter = (ShailIterator)list.iterator();
        while( iter.hasNext() )
        {
            int context = iter.nextNode();
            assertCountXPath( 3, context, "//processing-instruction()" );
            assertCountXPath( 2, context, "//processing-instruction('cheese')" );
            int result = assertCountXPath2( 1, context, "//processing-instruction('toast')" );
            assertValueOfXPath( "is tasty", result, "string()" );
        }
    }


    /* test evaluate() extension function
     */
    public void testid54032() throws JaxenException
    {
        Navigator nav = getNavigator();
        String url = "xml/evaluate.xml";
        log( "Document [" + url + "]" );
        int document = nav.getDocument( url );
        ShailXPath contextpath = new ShailXPath( "/", nav );
        log( "Initial Context :: " + contextpath );
        List list = contextpath.selectNodes( document );
        ShailIterator iter = (ShailIterator)list.iterator();
        while( iter.hasNext() )
        {
            int context = iter.nextNode();
            assertCountXPath( 3, context, "evaluate('//jumps/*')" );
            assertCountXPath( 1, context, "evaluate('//jumps/object/dog')" );
            assertCountXPath( 0, context, "evaluate('//jumps/object')/evaluate" ); 
            assertCountXPath( 1, context, "evaluate('//jumps/object')/dog" );
            assertCountXPath( 1, context, "evaluate('//jumps/*')/dog" );
            assertValueOfXPath( "brown", context, "evaluate( //metatest/@select )" );
            assertCountXPath( 1, context, "//metatest[ evaluate('//@color') = . ]" );
        }
    }


    public void testid54082() throws JaxenException
    {
        Navigator nav = getNavigator();
        String url = "xml/numbers.xml";
        log( "Document [" + url + "]" );
        int document = nav.getDocument( url );
        ShailXPath contextpath = new ShailXPath( "/numbers/set[1]", nav );
        log( "Initial Context :: " + contextpath );
        List list = contextpath.selectNodes( document );
        ShailIterator iter = (ShailIterator)list.iterator();
        while( iter.hasNext() )
        {
            int context = iter.nextNode();
            assertCountXPath( 1, context, "*[-3 = .]" );
            assertValueOfXPath( "true", context, "54 < *" );
            assertValueOfXPath( "true", context, "55 <= *" );
            assertValueOfXPath( "false", context, "69 < *" );
            assertValueOfXPath( "true", context, "-2 > *" );
            assertValueOfXPath( "true", context, "-3 >= *" );
            assertValueOfXPath( "false", context, "-4 >= *" );
        }
    }


    /* TODO
     This context should work, but needs a fixed version of saxpath to parse the right-hand side
     of the greater-than expression.
     <context select="/numbers/set[2]">
     <valueOf select="1 &gt; nr/@value">false</valueOf>
     <valueOf select="55 &gt; nr/@value">false</valueOf>
     <valueOf select="55 &gt;= nr/@value">true</valueOf>
     <valueOf select="1000000 &gt; nr/@value">true</valueOf>
     </context>
     
     */
    /* test sibling axes 
     */
    public void testid54145() throws JaxenException
    {
        Navigator nav = getNavigator();
        String url = "xml/axis.xml";
        log( "Document [" + url + "]" );
        int document = nav.getDocument( url );
        ShailXPath contextpath = new ShailXPath( "/root", nav );
        log( "Initial Context :: " + contextpath );
        List list = contextpath.selectNodes( document );
        ShailIterator iter = (ShailIterator)list.iterator();
        while( iter.hasNext() )
        {
            int context = iter.nextNode();
            assertCountXPath( 0, context, "preceding-sibling::*" );
        }
    }


    public void testid54156() throws JaxenException
    {
        Navigator nav = getNavigator();
        String url = "xml/axis.xml";
        log( "Document [" + url + "]" );
        int document = nav.getDocument( url );
        ShailXPath contextpath = new ShailXPath( "/root/a/a.3", nav );
        log( "Initial Context :: " + contextpath );
        List list = contextpath.selectNodes( document );
        ShailIterator iter = (ShailIterator)list.iterator();
        while( iter.hasNext() )
        {
            int context = iter.nextNode();
            assertCountXPath( 2, context, "preceding::*" );
        }
    }


    public void testid54168() throws JaxenException
    {
        Navigator nav = getNavigator();
        String url = "xml/axis.xml";
        log( "Document [" + url + "]" );
        int document = nav.getDocument( url );
        ShailXPath contextpath = new ShailXPath( "/root/a/a.3", nav );
        log( "Initial Context :: " + contextpath );
        List list = contextpath.selectNodes( document );
        ShailIterator iter = (ShailIterator)list.iterator();
        while( iter.hasNext() )
        {
            int context = iter.nextNode();
            assertCountXPath( 2, context, "preceding-sibling::*" );
        }
    }


    public void testid54180() throws JaxenException
    {
        Navigator nav = getNavigator();
        String url = "xml/axis.xml";
        log( "Document [" + url + "]" );
        int document = nav.getDocument( url );
        ShailXPath contextpath = new ShailXPath( "/", nav );
        log( "Initial Context :: " + contextpath );
        List list = contextpath.selectNodes( document );
        ShailIterator iter = (ShailIterator)list.iterator();
        while( iter.hasNext() )
        {
            int context = iter.nextNode();
            assertValueOfXPath( "a.2", context, "name(/root/a/a.3/preceding-sibling::*[1])" );
            assertValueOfXPath( "a.1", context, "name(/root/a/a.3/preceding-sibling::*[2])" );
        }
    }


    public void testid54197() throws JaxenException
    {
        Navigator nav = getNavigator();
        String url = "xml/axis.xml";
        log( "Document [" + url + "]" );
        int document = nav.getDocument( url );
        ShailXPath contextpath = new ShailXPath( "/", nav );
        log( "Initial Context :: " + contextpath );
        List list = contextpath.selectNodes( document );
        ShailIterator iter = (ShailIterator)list.iterator();
        while( iter.hasNext() )
        {
            int context = iter.nextNode();
            assertValueOfXPath( "a.4", context, "name(/root/a/a.3/following-sibling::*[1])" );
            assertValueOfXPath( "a.5", context, "name(/root/a/a.3/following-sibling::*[2])" );
        }
    }


    public void testid54219() throws JaxenException
    {
        Navigator nav = getNavigator();
        String url = "xml/web.xml";
        log( "Document [" + url + "]" );
        int document = nav.getDocument( url );
        ShailXPath contextpath = new ShailXPath( "/", nav );
        log( "Initial Context :: " + contextpath );
        List list = contextpath.selectNodes( document );
        ShailIterator iter = (ShailIterator)list.iterator();
        while( iter.hasNext() )
        {
            int context = iter.nextNode();
            assertValueOfXPath( "snoop", context, "/web-app/servlet[1]/servlet-name" );
            assertValueOfXPath( "snoop", context, "/web-app/servlet[1]/servlet-name/text()" );
            assertValueOfXPath( "file", context, "/web-app/servlet[2]/servlet-name" );
            assertValueOfXPath( "file", context, "/web-app/servlet[2]/servlet-name/text()" );
        }
    }


    public void testid54249() throws JaxenException
    {
        Navigator nav = getNavigator();
        String url = "xml/web.xml";
        log( "Document [" + url + "]" );
        int document = nav.getDocument( url );
        ShailXPath contextpath = new ShailXPath( "/web-app/servlet[1]", nav );
        log( "Initial Context :: " + contextpath );
        List list = contextpath.selectNodes( document );
        ShailIterator iter = (ShailIterator)list.iterator();
        while( iter.hasNext() )
        {
            int context = iter.nextNode();
            assertValueOfXPath( "snoop", context, "servlet-name" );
            assertValueOfXPath( "snoop", context, "servlet-name/text()" );
        }
    }


    public void testid54266() throws JaxenException
    {
        Navigator nav = getNavigator();
        String url = "xml/web.xml";
        log( "Document [" + url + "]" );
        int document = nav.getDocument( url );
        ShailXPath contextpath = new ShailXPath( "/web-app/servlet[2]/servlet-name", nav );
        log( "Initial Context :: " + contextpath );
        List list = contextpath.selectNodes( document );
        ShailIterator iter = (ShailIterator)list.iterator();
        while( iter.hasNext() )
        {
            int context = iter.nextNode();
            assertCountXPath( 3, context, "preceding::*" );
        }
    }


    public void testid54278() throws JaxenException
    {
        Navigator nav = getNavigator();
        String url = "xml/web.xml";
        log( "Document [" + url + "]" );
        int document = nav.getDocument( url );
        ShailXPath contextpath = new ShailXPath( "/web-app/servlet[2]/servlet-name", nav );
        log( "Initial Context :: " + contextpath );
        List list = contextpath.selectNodes( document );
        ShailIterator iter = (ShailIterator)list.iterator();
        while( iter.hasNext() )
        {
            int context = iter.nextNode();
            assertCountXPath( 13, context, "following::*" );
        }
    }


    /* test name
     */
    public void testid54298() throws JaxenException
    {
        Navigator nav = getNavigator();
        String url = "xml/web.xml";
        log( "Document [" + url + "]" );
        int document = nav.getDocument( url );
        ShailXPath contextpath = new ShailXPath( "/", nav );
        log( "Initial Context :: " + contextpath );
        List list = contextpath.selectNodes( document );
        ShailIterator iter = (ShailIterator)list.iterator();
        while( iter.hasNext() )
        {
            int context = iter.nextNode();
            int result = assertCountXPath2( 1, context, "*" );
            assertValueOfXPath( "web-app", result, "name()" );
            /* NOTE that the child::node() tests only work if the
             XML document does not comments or PIs

             */
            result = assertCountXPath2( 1, context, "./*" );
            assertValueOfXPath( "web-app", result, "name()" );
            result = assertCountXPath2( 1, context, "child::*" );
            assertValueOfXPath( "web-app", result, "name()" );
            result = assertCountXPath2( 1, context, "/*" );
            assertValueOfXPath( "web-app", result, "name()" );
            result = assertCountXPath2( 1, context, "/child::node()" );
            assertValueOfXPath( "web-app", result, "name(.)" );
            result = assertCountXPath2( 1, context, "child::node()" );
            assertValueOfXPath( "web-app", result, "name(.)" );
            /* empty names
             */
            assertValueOfXPath( "", context, "name()" );
            assertValueOfXPath( "", context, "name(.)" );
            assertValueOfXPath( "", context, "name(parent::*)" );
            assertValueOfXPath( "", context, "name(/)" );
            assertValueOfXPath( "", context, "name(/.)" );
            assertValueOfXPath( "", context, "name(/self::node())" );
            /* name of root elemet
             */
            assertValueOfXPath( "web-app", context, "name(node())" );
            assertValueOfXPath( "web-app", context, "name(/node())" );
            assertValueOfXPath( "web-app", context, "name(/*)" );
            assertValueOfXPath( "web-app", context, "name(/child::*)" );
            assertValueOfXPath( "web-app", context, "name(/child::node())" );
            assertValueOfXPath( "web-app", context, "name(/child::node())" );
            assertValueOfXPath( "web-app", context, "name(child::node())" );
            assertValueOfXPath( "web-app", context, "name(./*)" );
            assertValueOfXPath( "web-app", context, "name(*)" );
        }
    }


    public void testid54467() throws JaxenException
    {
        Navigator nav = getNavigator();
        String url = "xml/web.xml";
        log( "Document [" + url + "]" );
        int document = nav.getDocument( url );
        ShailXPath contextpath = new ShailXPath( "/*", nav );
        log( "Initial Context :: " + contextpath );
        List list = contextpath.selectNodes( document );
        ShailIterator iter = (ShailIterator)list.iterator();
        while( iter.hasNext() )
        {
            int context = iter.nextNode();
            /* empty names
             */
            assertValueOfXPath( "", context, "name(..)" );
            assertValueOfXPath( "", context, "name(parent::node())" );
            assertValueOfXPath( "", context, "name(parent::*)" );
            /* name of root elemet
             */
            assertValueOfXPath( "web-app", context, "name()" );
            assertValueOfXPath( "web-app", context, "name(.)" );
            assertValueOfXPath( "web-app", context, "name(../*)" );
            assertValueOfXPath( "web-app", context, "name(../child::node())" );
        }
    }


    /* test predicates
     */
    public void testid54522() throws JaxenException
    {
        Navigator nav = getNavigator();
        String url = "xml/nitf.xml";
        log( "Document [" + url + "]" );
        int document = nav.getDocument( url );
        ShailXPath contextpath = new ShailXPath( "/nitf/head/docdata", nav );
        log( "Initial Context :: " + contextpath );
        List list = contextpath.selectNodes( document );
        ShailIterator iter = (ShailIterator)list.iterator();
        while( iter.hasNext() )
        {
            int context = iter.nextNode();
            assertCountXPath( 1, context, "doc-id[@regsrc='AP' and @id-string='D76UIMO80']" );
        }
    }


    public void testid54534() throws JaxenException
    {
        Navigator nav = getNavigator();
        String url = "xml/nitf.xml";
        log( "Document [" + url + "]" );
        int document = nav.getDocument( url );
        ShailXPath contextpath = new ShailXPath( "/nitf/head", nav );
        log( "Initial Context :: " + contextpath );
        List list = contextpath.selectNodes( document );
        ShailIterator iter = (ShailIterator)list.iterator();
        while( iter.hasNext() )
        {
            int context = iter.nextNode();
            assertCountXPath( 1, context, "meta[@name='ap-cycle']" );
            assertCountXPath( 1, context, "meta[@content='AP']" );
            assertCountXPath( 8, context, "meta[@name and @content]" );
            assertCountXPath( 1, context, "meta[@name='ap-cycle' and @content='AP']" );
            assertCountXPath( 7, context, "meta[@name != 'ap-cycle']" );
        }
    }


    public void testid54570() throws JaxenException
    {
        Navigator nav = getNavigator();
        String url = "xml/nitf.xml";
        log( "Document [" + url + "]" );
        int document = nav.getDocument( url );
        ShailXPath contextpath = new ShailXPath( "/", nav );
        log( "Initial Context :: " + contextpath );
        List list = contextpath.selectNodes( document );
        ShailIterator iter = (ShailIterator)list.iterator();
        while( iter.hasNext() )
        {
            int context = iter.nextNode();
            assertCountXPath( 1, context, "/nitf/head/meta[@name='ap-cycle']" );
            assertCountXPath( 1, context, "/nitf/head/meta[@content='AP']" );
            assertCountXPath( 8, context, "/nitf/head/meta[@name and @content]" );
            assertCountXPath( 1, context, "/nitf/head/meta[@name='ap-cycle' and @content='AP']" );
            assertCountXPath( 7, context, "/nitf/head/meta[@name != 'ap-cycle']" );
        }
    }


    public void testid54614() throws JaxenException
    {
        Navigator nav = getNavigator();
        String url = "xml/moreover.xml";
        log( "Document [" + url + "]" );
        int document = nav.getDocument( url );
        ShailXPath contextpath = new ShailXPath( "/", nav );
        log( "Initial Context :: " + contextpath );
        List list = contextpath.selectNodes( document );
        ShailIterator iter = (ShailIterator)list.iterator();
        while( iter.hasNext() )
        {
            int context = iter.nextNode();
            assertCountXPath( 1, context, "/child::node()" );
            assertCountXPath( 1, context, "/*" );
            assertCountXPath( 20, context, "/*/article" );
            assertCountXPath( 221, context, "//*" );
            assertCountXPath( 20, context, "//*[local-name()='article']" );
            assertCountXPath( 20, context, "//article" );
            assertCountXPath( 20, context, "/*/*[@code]" );
            assertCountXPath( 1, context, "/moreovernews/article[@code='13563275']" );
            ShailXPath xpath = new ShailXPath( "/moreovernews/article[@code='13563275']" );
            ShailList results = (ShailList)xpath.selectNodes( getContext( context ) );
            int result = results.getInt( 0 );
            assertValueOfXPath( "http://c.moreover.com/click/here.pl?x13563273", result, "url" );
            xpath = new ShailXPath( "/*/article[@code='13563275']" );
            results = (ShailList)xpath.selectNodes( getContext( context ) );
            result = results.getInt( 0 );
            assertValueOfXPath( "http://c.moreover.com/click/here.pl?x13563273", result, "url" );
            xpath = new ShailXPath( "//article[@code='13563275']" );
            results = (ShailList)xpath.selectNodes( getContext( context ) );
            result = results.getInt( 0 );
            assertValueOfXPath( "http://c.moreover.com/click/here.pl?x13563273", result, "url" );
            xpath = new ShailXPath( "//*[@code='13563275']" );
            results = (ShailList)xpath.selectNodes( getContext( context ) );
            result = results.getInt( 0 );
            assertValueOfXPath( "http://c.moreover.com/click/here.pl?x13563273", result, "url" );
            xpath = new ShailXPath( "/child::node()/child::node()[@code='13563275']" );
            results = (ShailList)xpath.selectNodes( getContext( context ) );
            result = results.getInt( 0 );
            assertValueOfXPath( "http://c.moreover.com/click/here.pl?x13563273", result, "url" );
            xpath = new ShailXPath( "/*/*[@code='13563275']" );
            results = (ShailList)xpath.selectNodes( getContext( context ) );
            result = results.getInt( 0 );
            assertValueOfXPath( "http://c.moreover.com/click/here.pl?x13563273", result, "url" );
        }
    }


    /* test other node types
     */
    public void testNodeTypes() throws JaxenException
    {
        Navigator nav = getNavigator();
        String url = "xml/contents.xml";
        log( "Document [" + url + "]" );
        int document = nav.getDocument( url );
        ShailXPath contextpath = new ShailXPath( "/", nav );
        log( "Initial Context :: " + contextpath );
        List list = contextpath.selectNodes( document );
        ShailIterator iter = (ShailIterator)list.iterator();
        while( iter.hasNext() )
        {
            int context = iter.nextNode();
            assertCountXPath( 3, context, "processing-instruction()" );
            assertCountXPath( 3, context, "/processing-instruction()" );
            assertCountXPath( 1, context, "/comment()" );
            assertCountXPath( 1, context, "comment()" );
            assertCountXPath( 2, context, "/child::node()/comment()" );
            assertCountXPath( 2, context, "/*/comment()" );
            assertCountXPath( 3, context, "//comment()" );
        }
    }


    /* test positioning
     */
    public void testPositioning() throws JaxenException
    {
        Navigator nav = getNavigator();
        String url = "xml/fibo.xml";
        log( "Document [" + url + "]" );
        int document = nav.getDocument( url );
        ShailXPath contextpath = new ShailXPath( "/", nav );
        log( "Initial Context :: " + contextpath );
        List list = contextpath.selectNodes( document );
        ShailIterator iter = (ShailIterator)list.iterator();
        while( iter.hasNext() )
        {
            int context = iter.nextNode();
            assertCountXPath( 9, context, "/*/fibonacci[position() < 10]" );
            assertCountXPath( 26, context, "//fibonacci" );
            assertValueOfXPath( "196417", context, "sum(//fibonacci)" );
            assertValueOfXPath( "325", context, "sum(//fibonacci/@index)" );
            assertValueOfXPath( "1", context, "/*/fibonacci[2]" );
            assertValueOfXPath( "75025", context, "/*/fibonacci[ count(/*/fibonacci) ]" );
            assertValueOfXPath( "46368", context, "/*/fibonacci[ count(/*/fibonacci) - 1 ]" );
        }
    }


    /* test number functions
     */
    /* test Axes 
     */
    public void testid54853() throws JaxenException
    {
        Navigator nav = getNavigator();
        String url = "xml/web.xml";
        log( "Document [" + url + "]" );
        int document = nav.getDocument( url );
        ShailXPath contextpath = new ShailXPath( "/", nav );
        log( "Initial Context :: " + contextpath );
        List list = contextpath.selectNodes( document );
        ShailIterator iter = (ShailIterator)list.iterator();
        while( iter.hasNext() )
        {
            int context = iter.nextNode();
            assertCountXPath( 19, context, "descendant-or-self::*" );
            assertCountXPath( 19, context, "descendant::*" );
            assertCountXPath( 19, context, "/descendant::*" );
            assertCountXPath( 19, context, "/descendant-or-self::*" );
            assertCountXPath( 2, context, "/descendant::servlet" );
            assertCountXPath( 2, context, "/descendant-or-self::servlet" );
            assertCountXPath( 2, context, "descendant-or-self::servlet" );
            assertCountXPath( 2, context, "descendant::servlet" );
            assertCountXPath( 2, context, "/*/servlet" );
            assertValueOfXPath( "2", context, "count(/*/servlet)" );
            assertCountXPath( 2, context, "//servlet" );
            assertValueOfXPath( "2", context, "count(//servlet)" );
        }
    }


    public void testid54932() throws JaxenException
    {
        Navigator nav = getNavigator();
        String url = "xml/web.xml";
        log( "Document [" + url + "]" );
        int document = nav.getDocument( url );
        ShailXPath contextpath = new ShailXPath( "/web-app", nav );
        log( "Initial Context :: " + contextpath );
        List list = contextpath.selectNodes( document );
        ShailIterator iter = (ShailIterator)list.iterator();
        while( iter.hasNext() )
        {
            int context = iter.nextNode();
            assertCountXPath( 2, context, "/descendant::servlet" );
            assertCountXPath( 2, context, "/descendant-or-self::servlet" );
            assertCountXPath( 2, context, "descendant-or-self::servlet" );
            assertCountXPath( 2, context, "descendant::servlet" );
        }
    }


    public void testCountFunction() throws JaxenException
    {
        Navigator nav = getNavigator();
        String url = "xml/much_ado.xml";
        log( "Document [" + url + "]" );
        int document = nav.getDocument( url );
        ShailXPath contextpath = new ShailXPath( "/", nav );
        log( "Initial Context :: " + contextpath );
        List list = contextpath.selectNodes( document );
        ShailIterator iter = (ShailIterator)list.iterator();
        while( iter.hasNext() )
        {
            int context = iter.nextNode();
            
            assertCountXPath( 5, context, "/descendant::ACT" );
            assertCountXPath( 5, context, "descendant::ACT" );
            assertValueOfXPath( "Much Ado about Nothing", context, "/PLAY/TITLE" );
            assertValueOfXPath( "4", context, "2+2" );
            assertValueOfXPath( "21", context, "5 * 4 + 1" );
            assertValueOfXPath( "5", context, "count(descendant::ACT)" );
            assertValueOfXPath( "35", context, "10 + count(descendant::ACT) * 5" );
            assertValueOfXPath( "75", context, "(10 + count(descendant::ACT)) * 5" );
        }
    }


    public void testCountFunctionMore() throws JaxenException
    {
        Navigator nav = getNavigator();
        String url = "xml/much_ado.xml";
        log( "Document [" + url + "]" );
        int document = nav.getDocument( url );
        ShailXPath contextpath = new ShailXPath( "/PLAY/ACT[2]/SCENE[1]", nav );
        log( "Initial Context :: " + contextpath );
        List list = contextpath.selectNodes( document );
        ShailIterator iter = (ShailIterator)list.iterator();
        while( iter.hasNext() )
        {
            int context = iter.nextNode();
            assertCountXPath( 5, context, "/descendant::ACT" );
            assertCountXPath( 5, context, "../../descendant::ACT" );
            assertCountXPath( 141, context, "/PLAY/ACT[2]/SCENE[1]/descendant::SPEAKER" );
            assertCountXPath( 141, context, "descendant::SPEAKER" );
            assertValueOfXPath( "646", context, "count(descendant::*)+1" );
            assertValueOfXPath( "142", context, "count(descendant::SPEAKER)+1" );
            assertValueOfXPath( "2", context, "count(ancestor::*)" );
            assertValueOfXPath( "1", context, "count(ancestor::PLAY)" );
            assertValueOfXPath( "3", context, "count(ancestor-or-self::*)" );
            assertValueOfXPath( "1", context, "count(ancestor-or-self::PLAY)" );
            assertValueOfXPath( "6", context, "5+count(ancestor::*)-1" );
        }
    }


    public void testCorrectPredicateApplication() throws JaxenException
    {
        Navigator nav = getNavigator();
        String url = "xml/much_ado.xml";
        log( "Document [" + url + "]" );
        int document = nav.getDocument( url );
        ShailXPath contextpath = new ShailXPath( "/", nav );
        log( "Initial Context :: " + contextpath );
        List list = contextpath.selectNodes( document );
        ShailIterator iter = (ShailIterator)list.iterator();
        while( iter.hasNext() )
        {
            int context = iter.nextNode();
            /* Test correct predicate application
             */
            assertValueOfXPath( "5", context, "count(/PLAY/ACT/SCENE[1])" );
        }
    }


    /* test axis node ordering
     */
    public void testAxisNodeOrdering() throws JaxenException
    {
        Navigator nav = getNavigator();
        String url = "xml/web.xml";
        log( "Document [" + url + "]" );
        int document = nav.getDocument( url );
        ShailXPath contextpath = new ShailXPath( "/", nav );
        log( "Initial Context :: " + contextpath );
        List list = contextpath.selectNodes( document );
        ShailIterator iter = (ShailIterator)list.iterator();
        while( iter.hasNext() )
        {
            int context = iter.nextNode();
            /* Reported as Jira issue JAXEN-24
             */
            assertCountXPath( 1, context,
                    "//servlet-mapping/preceding::*[1][name()='description']" );
            assertCountXPath( 1, context,
                    "/web-app/servlet//description/following::*[1][name()='servlet-mapping']" );
            assertCountXPath( 1, context,
                    "/web-app/servlet//description/following::*[2][name()='servlet-name']" );
        }
    }


    /* test document function
     */
    public void testDocumentFunction1() throws JaxenException
    {
        Navigator nav = getNavigator();
        String url = "xml/text.xml";
        log( "Document [" + url + "]" );
        int document = nav.getDocument( url );
        ShailXPath contextpath = new ShailXPath( "/", nav );
        log( "Initial Context :: " + contextpath );
        List list = contextpath.selectNodes( document );
        ShailIterator iter = (ShailIterator)list.iterator();
        while( iter.hasNext() )
        {
            int context = iter.nextNode();
            int result = assertCountXPath2( 1, context, "document('xml/web.xml')" );
            assertValueOfXPath( "snoop", result, "/web-app/servlet[1]/servlet-name" );
            assertValueOfXPath( "snoop", result, "/web-app/servlet[1]/servlet-name/text()" );

            assertValueOfXPath( "snoop", context,
                    "document('xml/web.xml')/web-app/servlet[1]/servlet-name" );
        }
    }


    /* Test to check if the context changes when an extension function is used.
     First test is an example, second is the actual test.
     
     */
    public void testDocumentFunctionContextExample() throws JaxenException
    {
        Navigator nav = getNavigator();
        String url = "xml/text.xml";
        log( "Document [" + url + "]" );
        int document = nav.getDocument( url );
        ShailXPath contextpath = new ShailXPath( "/foo/bar/cheese[1]", nav );
        log( "Initial Context :: " + contextpath );
        List list = contextpath.selectNodes( document );
        ShailIterator iter = (ShailIterator)list.iterator();
        while( iter.hasNext() )
        {
            int context = iter.nextNode();
            
//            logger.debug( "ROOT="+ShailNavigator.getInstance().getModel().toString( new Integer(0) ));
//            logger.debug( "node 17="+ShailNavigator.getInstance().getModel().toString( new Integer(17) ));
            
            assertValueOfXPath( "snoop", context, "document('xml/web.xml')/web-app/servlet[1]/servlet-name" );
//            
//            logger.debug( "ROOT="+ShailNavigator.getInstance().getModel().toString( new Integer(0) ));
//            logger.debug( "node 13="+ShailNavigator.getInstance().getModel().toString( new Integer(13) ));
            
            assertValueOfXPath( "3snoop", context,
            "concat(./@id,document('xml/web.xml')/web-app/servlet[1]/servlet-name )" );
            
            assertValueOfXPath( "3", context,
            "./@id" );            
//            
            assertValueOfXPath( "snoop", context,
            "document('xml/web.xml')/web-app/servlet[1]/servlet-name" );            

//            logger.debug( "node 17="+ShailNavigator.getInstance().getModel().toString( new Integer(17) ));            
//            logger.debug( "node 17 string value="+ShailNavigator.getInstance().getModel().getStringValue( new Integer(17) ));
            
            assertValueOfXPath( "3snoop3", context,
                    "concat(./@id,document('xml/web.xml')/web-app/servlet[1]/servlet-name,./@id)" );
            
        }
    }


    public void testDocumentFunctionActual() throws JaxenException
    {
        Navigator nav = getNavigator();
        String url = "xml/message.xml";
        log( "Document [" + url + "]" );
        int document = nav.getDocument( url );
        ShailXPath contextpath = new ShailXPath( "/", nav );
        log( "Initial Context :: " + contextpath );
        List list = contextpath.selectNodes( document );
        ShailIterator iter = (ShailIterator)list.iterator();
        while( iter.hasNext() )
        {
            int context = iter.nextNode();
            assertValueOfXPath( "Pruefgebiete", context,
                    "/message/body/data/items/item[name/text()='parentinfo']/value" );
            assertValueOfXPath( "Pruefgebiete", context,
                    "document('xml/message.xml')/message/body/data/items/item[name/text()='parentinfo']/value" );
        }
    }


    /* test behaviour of AbsoluteLocationPath
     */
    public void testAbsoluteLocationPaths() throws JaxenException
    {
        Navigator nav = getNavigator();
        String url = "xml/simple.xml";
        log( "Document [" + url + "]" );
        int document = nav.getDocument( url );
        ShailXPath contextpath = new ShailXPath( "/root/a", nav );
        log( "Initial Context :: " + contextpath );
        List list = contextpath.selectNodes( document );
        ShailIterator iter = (ShailIterator)list.iterator();
        while( iter.hasNext() )
        {
            int context = iter.nextNode();
            assertValueOfXPath( "ab", context, "concat( ., /root/b )" );
            assertValueOfXPath( "ba", context, "concat( ../b, . )" );
            assertValueOfXPath( "ba", context, "concat( /root/b, . )" );
            assertValueOfXPath( "db", context, "concat( /root/c/d, ../b )" );
        }
    }


    /* test the translate() function
     */
    public void testTranslateFunction() throws JaxenException
    {
        Navigator nav = getNavigator();
        String url = "xml/simple.xml";
        log( "Document [" + url + "]" );
        int document = nav.getDocument( url );
        ShailXPath contextpath = new ShailXPath( "/", nav );
        log( "Initial Context :: " + contextpath );
        List list = contextpath.selectNodes( document );
        ShailIterator iter = (ShailIterator)list.iterator();
        while( iter.hasNext() )
        {
            int context = iter.nextNode();
            assertValueOfXPath( "", context, "translate( '', '', '' )" );
            assertValueOfXPath( "abcd", context, "translate( 'abcd', '', '' )" );
            assertValueOfXPath( "abcd", context, "translate( 'abcd', 'abcd', 'abcd' )" );
            assertValueOfXPath( "abcd", context, "translate( 'abcd', 'dcba', 'dcba' )" );
            assertValueOfXPath( "dcba", context, "translate( 'abcd', 'abcd', 'dcba' )" );
            assertValueOfXPath( "ab", context, "translate( 'abcd', 'abcd', 'ab' )" );
            assertValueOfXPath( "cd", context, "translate( 'abcd', 'cdab', 'cd' )" );
            assertValueOfXPath( "xy", context, "translate( 'abcd', 'acbd', 'xy' )" );
            assertValueOfXPath( "abcd", context, "translate( 'abcd', 'abcdb', 'abcdb' )" );
            assertValueOfXPath( "abcd", context, "translate( 'abcd', 'abcd', 'abcdb' )" );
        }
    }


    public void testSubstringFunction() throws JaxenException
    {
        Navigator nav = getNavigator();
        String url = "xml/simple.xml";
        log( "Document [" + url + "]" );
        int document = nav.getDocument( url );
        ShailXPath contextpath = new ShailXPath( "/", nav );
        log( "Initial Context :: " + contextpath );
        List list = contextpath.selectNodes( document );
        ShailIterator iter = (ShailIterator)list.iterator();
        while( iter.hasNext() )
        {
            int context = iter.nextNode();
            assertValueOfXPath( "234", context, "substring('12345', 1.5, 2.6)" );
            assertValueOfXPath( "12", context, "substring('12345', 0, 3)" );
            assertValueOfXPath( "", context, "substring('12345', 0 div 0, 3)" );
            assertValueOfXPath( "", context, "substring('12345', 1, 0 div 0)" );
            assertValueOfXPath( "12345", context, "substring('12345', -42, 1 div 0)" );
            assertValueOfXPath( "", context, "substring('12345', -1 div 0, 1 div 0)" );
            assertValueOfXPath( "345", context, "substring('12345', 3)" );
            assertValueOfXPath( "12345", context, "substring('12345',1,15)" );
        }
    }


    /* Some tests for the normalize-space() function
     */
    public void testNormalizeSpaceFunction() throws JaxenException
    {
        Navigator nav = getNavigator();
        String url = "xml/simple.xml";
        log( "Document [" + url + "]" );
        int document = nav.getDocument( url );
        ShailXPath contextpath = new ShailXPath( "/", nav );
        log( "Initial Context :: " + contextpath );
        List list = contextpath.selectNodes( document );
        ShailIterator iter = (ShailIterator)list.iterator();
        while( iter.hasNext() )
        {
            int context = iter.nextNode();
            assertValueOfXPath( "abc", context, "normalize-space('    abc    ')" );
            assertValueOfXPath( "a b c", context, "normalize-space(' a  b  c  ')" );
            assertValueOfXPath( "a b c", context, "normalize-space(' a \n b \n  c')" );
            /* Next test case addresses issue JAXEN-22
             */
            assertValueOfXPath( "", context, "normalize-space(' ')" );
            /* Next test case addresses issue JAXEN-29
             */
            assertValueOfXPath( "", context, "normalize-space('')" );
        }
    }


    /* test cases for String extension functions
     */
    public void testStringExtensionFunctions() throws JaxenException
    {
        Navigator nav = getNavigator();
        String url = "xml/web.xml";
        log( "Document [" + url + "]" );
        int document = nav.getDocument( url );
        ShailXPath contextpath = new ShailXPath( "/web-app/servlet[1]", nav );
        log( "Initial Context :: " + contextpath );
        List list = contextpath.selectNodes( document );
        ShailIterator iter = (ShailIterator)list.iterator();
        while( iter.hasNext() )
        {
            int context = iter.nextNode();
            assertValueOfXPath( "SNOOPSERVLET", context, "upper-case( servlet-class )" );
            assertValueOfXPath( "snoopservlet", context, "lower-case( servlet-class )" );
            assertValueOfXPath( "SNOOPSERVLET", context, "upper-case( servlet-class, 'fr' )" );
            assertValueOfXPath( "SNOOPSERVLET", context, "upper-case( servlet-class, 'fr-CA' )" );
            assertValueOfXPath( "SNOOPSERVLET", context,
                    "upper-case( servlet-class, 'es-ES-Traditional_WIN' )" );
            assertValueOfXPath( "true", context, "ends-with( servlet-class, 'Servlet' )" );
            assertValueOfXPath( "false", context, "ends-with( servlet-class, 'S' )" );
        }
    }


    /* test cases for the lang() function
     */
    public void testLangFunction() throws JaxenException
    {
        Navigator nav = getNavigator();
        String url = "xml/lang.xml";
        log( "Document [" + url + "]" );
        int document = nav.getDocument( url );
        ShailXPath contextpath = new ShailXPath( "/", nav );
        log( "Initial Context :: " + contextpath );
        List list = contextpath.selectNodes( document );
        ShailIterator iter = (ShailIterator)list.iterator();
        while( iter.hasNext() )
        {
            int context = iter.nextNode();
            assertCountXPath( 0, context, "/e1/e2[lang('hr')]" );
            assertCountXPath( 1, context, "/e1/e2/e3[lang('en')]" );
            assertCountXPath( 1, context, "/e1/e2/e3[lang('en-US')]" );
            assertCountXPath( 0, context, "/e1/e2/e3[lang('en-GB')]" );
            assertCountXPath( 2, context, "/e1/e2/e3[lang('hu')]" );
            assertCountXPath( 0, context, "/e1/e2/e3[lang('hu-HU')]" );
            assertCountXPath( 1, context, "/e1/e2/e3[lang('es')]" );
            assertCountXPath( 0, context, "/e1/e2/e3[lang('es-BR')]" );
        }
    }


    /* test namespace
     */
    public void testNamespacesAgain() throws JaxenException
    {
        Navigator nav = getNavigator();
        String url = "xml/namespaces.xml";
        log( "Document [" + url + "]" );
        int document = nav.getDocument( url );
        ShailXPath contextpath = new ShailXPath( "/", nav );
        log( "Initial Context :: " + contextpath );
        List list = contextpath.selectNodes( document );
        SimpleNamespaceContext nsContext = new SimpleNamespaceContext();
        nsContext.addNamespace( "alias", "http://fooNamespace/" );
        nsContext.addNamespace( "bar", "http://barNamespace/" );
        nsContext.addNamespace( "voo", "http://fooNamespace/" );
        nsContext.addNamespace( "foo", "http://fooNamespace/" );
        getContextSupport().setNamespaceContext( nsContext );
        ShailIterator iter = (ShailIterator)list.iterator();
        while( iter.hasNext() )
        {
            int context = iter.nextNode();
            assertCountXPath( 1, context, "/*" );
            assertCountXPath( 1, context, "/foo:a" );
            assertCountXPath( 1, context, "/foo:a/b" );
            assertCountXPath( 1, context, "/voo:a/b/c" );
            assertCountXPath( 1, context, "/voo:a/bar:f" );
            assertCountXPath( 1, context,
                    "/*[namespace-uri()='http://fooNamespace/' and local-name()='a']" );
            assertCountXPath(
                    1,
                    context,
                    "/*[local-name()='a' and namespace-uri()='http://fooNamespace/']/*[local-name()='x' and namespace-uri()='http://fooNamespace/']" );
            assertCountXPath(
                    1,
                    context,
                    "/*[local-name()='a' and namespace-uri()='http://fooNamespace/']/*[local-name()='x' and namespace-uri()='http://fooNamespace/']/*[local-name()='y' and namespace-uri()='http://fooNamespace/']" );
        }
    }


    /* the prefix here and in the document have no relation; it's their
     namespace-uri binding that counts 
     */
    public void testPrefixDoesntMatter() throws JaxenException
    {
        Navigator nav = getNavigator();
        String url = "xml/namespaces.xml";
        log( "Document [" + url + "]" );
        int document = nav.getDocument( url );
        ShailXPath contextpath = new ShailXPath( "/", nav );
        log( "Initial Context :: " + contextpath );
        List list = contextpath.selectNodes( document );
        SimpleNamespaceContext nsContext = new SimpleNamespaceContext();
        nsContext.addNamespace( "foo", "http://somethingElse/" );
        getContextSupport().setNamespaceContext( nsContext );
        ShailIterator iter = (ShailIterator)list.iterator();
        while( iter.hasNext() )
        {
            int context = iter.nextNode();
            assertCountXPath( 0, context, "/foo:a/b/c" );
        }
    }


    /* Jaxen-67, affects Jelly and Maven */
    public void testCDATASectionsAreIncludedInTextNodes() throws JaxenException
    {
        Navigator nav = getNavigator();
        String url = "xml/cdata.xml";
        log( "Document [" + url + "]" );
        int document = nav.getDocument( url );
        ShailXPath contextPath = new ShailXPath( "/p/text()", nav );
        log( "Initial Context :: " + contextPath );
        List list = contextPath.selectNodes( document );
        // Depending on the object model, there can be anywhere from 
        // 1 to 3 nodes returned here.
        StringBuffer buffer = new StringBuffer( 10 );
        ShailIterator iterator = (ShailIterator)list.iterator();
        while( iterator.hasNext() )
        {
            buffer.append( StringFunction.evaluate( iterator.nextNode(), nav ) );
        }
        assertEquals( "awhateverb", buffer.toString() );
    }


    public void testNamespaces() throws JaxenException
    {
        Navigator nav = getNavigator();
        String url = "xml/namespaces.xml";
        log( "Document [" + url + "]" );
        int document = nav.getDocument( url );
        
        ShailXPath contextpath = new ShailXPath( "/", nav );
        log( "Initial Context :: " + contextpath );
        List list = contextpath.selectNodes( document );
        SimpleNamespaceContext nsContext = new SimpleNamespaceContext();
        nsContext.addNamespace( "alias", "http://fooNamespace/" );
        nsContext.addNamespace( "bar", "http://barNamespace/" );
        nsContext.addNamespace( "foo", "http://fooNamespace/" );
        getContextSupport().setNamespaceContext( nsContext );
        ShailIterator iter = (ShailIterator)list.iterator();
        while( iter.hasNext() )
        {
            int context = iter.nextNode();
            assertCountXPath( 1, context, "/foo:a" );
            assertValueOfXPath( "Hello", context, "/foo:a/b/c" );
            assertValueOfXPath( "Hey", context, "/foo:a/foo:d/foo:e" );
            assertValueOfXPath( "Hey3", context, "/foo:a/alias:x/alias:y" );
            assertValueOfXPath( "Hey3", context, "/foo:a/foo:x/foo:y" );
            assertValueOfXPath(
                    "Hey3",
                    context,
                    "/*[local-name()='a' and namespace-uri()='http://fooNamespace/']/*[local-name()='x' and namespace-uri()='http://fooNamespace/']/*[local-name()='y' and namespace-uri()='http://fooNamespace/']" );
        }
    }


    public void testNoNamespace() throws JaxenException
    {
        Navigator nav = getNavigator();
        String url = "xml/defaultNamespace.xml";
        log( "Document [" + url + "]" );
        int document = nav.getDocument( url );
        ShailXPath contextpath = new ShailXPath( "/", nav );
        log( "Initial Context :: " + contextpath );
        List list = contextpath.selectNodes( document );
        ShailIterator iter = (ShailIterator)list.iterator();
        while( iter.hasNext() )
        {
            int context = iter.nextNode();
            /* NOTE: /a/b/c selects elements in no namespace only!
             */
            assertCountXPath( 0, context, "/a/b/c" );
            /*
             The following test uses an unbound prefix 'x' and should throw an exception.
             Addresses issue JAXEN-18.
             Turns out this isn't really tested as the test didn't fail when the exception wasn't thrown.
             <test select="/x:a/x:b/x:c" count="0" exception="true"/>

             */
        }
    }


    public void testNamespaceResolution() throws JaxenException
    {
        Navigator nav = getNavigator();
        String url = "xml/defaultNamespace.xml";
        log( "Document [" + url + "]" );
        int document = nav.getDocument( url );
        
        ShailXPath contextpath = new ShailXPath( "/", nav );
        log( "Initial Context :: " + contextpath );
        List list = contextpath.selectNodes( document );
        SimpleNamespaceContext nsContext = new SimpleNamespaceContext();
        nsContext.addNamespace( "dummy", "http://dummyNamespace/" );
        getContextSupport().setNamespaceContext( nsContext );
        ShailIterator iter = (ShailIterator)list.iterator();
        while( iter.hasNext() )
        {
            int context = iter.nextNode();
            assertCountXPath( 1, context, "/dummy:a/dummy:b/dummy:c" );
            assertCountXPath( 1, context, "/*[namespace-uri()='http://dummyNamespace/']" );
        }
    }


    public void testTextNodes() throws JaxenException
    {
        Navigator nav = getNavigator();
        String url = "xml/text.xml";
        log( "Document [" + url + "]" );
        int document = nav.getDocument( url );
        ShailXPath contextpath = new ShailXPath( "/", nav );
        log( "Initial Context :: " + contextpath );
        List list = contextpath.selectNodes( document );
        ShailIterator iter = (ShailIterator)list.iterator();
        while( iter.hasNext() )
        {
            int context = iter.nextNode();
            assertCountXPath( 3, context, "/foo/bar/text()" );
            assertValueOfXPath( "baz", context, "normalize-space(/foo/bar/text())" );
        }
    }


    public void testNamespaceNodeCounts1() throws JaxenException
    {
        Navigator nav = getNavigator();
        String url = "xml/testNamespaces.xml";
        log( "Document [" + url + "]" );
        int document = nav.getDocument( url );
        ShailXPath contextpath = new ShailXPath( "/", nav );
        log( "Initial Context :: " + contextpath );
        List list = contextpath.selectNodes( document );
        ShailIterator iter = (ShailIterator)list.iterator();
        while( iter.hasNext() )
        {
            int context = iter.nextNode();
            /* the root is not an element, so no namespaces
             */
            assertCountXPath( 0, context, "namespace::*" );
            assertCountXPath( 0, context, "/namespace::*" );
            /* must count the default xml: prefix as well
             */
            assertCountXPath( 3, context, "/Template/Application1/namespace::*" );
            
            assertCountXPath( 0, context, "/Template/Application1/namespace::xpl[ . != 'http://www.xxxx.com/' ]" );
            assertCountXPath( 0, context, "/Template/Application1/namespace::xplt[ . != 'http://www.xxxx.com/' ]" );
            
            assertCountXPath( 3, context, "/Template/Application2/namespace::*" );
            /* every element has separate copies
             */
            assertCountXPath( 25, context, "//namespace::*" );
        }
    }


    public void testNamespaceNodeCounts() throws JaxenException
    {
        Navigator nav = getNavigator();
        String url = "xml/testNamespaces.xml";
        log( "Document [" + url + "]" );
        int document = nav.getDocument( url );
        ShailXPath contextpath = new ShailXPath( "/Template/Application1", nav );
        log( "Initial Context :: " + contextpath );
        List list = contextpath.selectNodes( document );
        
        //DEBUG
//        contextpath = new ShailXPath( "//*[local-name()='insertText' and namespace-uri()='http://www.xxxx.com/']", nav );
//        List list2 = contextpath.selectNodes( document );
        
        ShailIterator iter = (ShailIterator)list.iterator();
        while( iter.hasNext() )
        {
            int context = iter.nextNode();
            /* must count the default xml: prefix as well
             */
            assertCountXPath( 3, context, "namespace::*" );
            assertCountXPath( 0, context, "/namespace::*" );
            assertCountXPath( 3, context, "/Template/Application1/namespace::*" );
            assertCountXPath( 3, context, "/Template/Application2/namespace::*" );
            
                        
//            logger.debug(list2);  //DEBUG            
            assertCountXPath( 9, context, "//namespace::*[.='http://www.w3.org/XML/1998/namespace']" );
            assertCountXPath( 16, context, "//namespace::*[.='http://www.xxxx.com/']" );
            
            assertCountXPath( 25, context, "//namespace::*" );//HERE!
            assertCountXPath( 8, context, "//namespace::xplt" );
            /* the name test literally matches the prefix as given in the
             document, and does not use the uri
             */
            assertCountXPath( 0, context, "//namespace::somethingelse" );
        }
    }


    public void testNamespaceNodesHaveParent() throws JaxenException
    {
        Navigator nav = getNavigator();
        String url = "xml/testNamespaces.xml";
        log( "Document [" + url + "]" );
        int document = nav.getDocument( url );
        ShailXPath contextpath = new ShailXPath( "/", nav );
        log( "Initial Context :: " + contextpath );
        List list = contextpath.selectNodes( document );
        ShailIterator iter = (ShailIterator)list.iterator();
        while( iter.hasNext() )
        {
            int context = iter.nextNode();
            /* namespace nodes have their element as their parent
             */
            logger.debug( "testNamespaceNodesHaveParent context="+context );
            assertCountXPath( 1, context, "/Template/namespace::xml/parent::Template" );
        }
    }


    /* namespace nodes can also be used as context nodes
     */
    public void testNamespaceNodeAsContext() throws JaxenException
    {
        Navigator nav = getNavigator();
        String url = "xml/testNamespaces.xml";
        log( "Document [" + url + "]" );
        int document = nav.getDocument( url );
        ShailXPath contextpath = new ShailXPath( "/Template/namespace::xml", nav );
        log( "Initial Context :: " + contextpath );
        List list = contextpath.selectNodes( document );
        ShailIterator iter = (ShailIterator)list.iterator();
        while( iter.hasNext() )
        {
            int context = iter.nextNode();
            assertCountXPath( 1, context, "parent::Template" );
        }
    }
}